#include "ok_led.h"

/* Taille de la table des pages de niv 1 en octets */
#define FIRST_LVL_TT_SIZE	16384U
/* La taille d'une section est de 1 MB */
#define SECTION_SIZE 		1048576U
/* Nombre d'entrées dans la table des pages de niv 1 */
#define FIRST_LVL_TT_COUN	4096U
/* Taille d'une table des pages de niv 2 en octets */
#define SECON_LVL_TT_SIZE	1024U
/* Nombre d'entrées dans la table des pages de niv 2 */
#define SECON_LVL_TT_COUN	256U
/* La taille d'une page est de 4kB */
#define PAGE_SIZE			4096U
/* Taille de la table des pages, niveau 1 et niveaux 2 compris */
#define TOTAL_TT_SIZE (FIRST_LVL_TT_SIZE+(FIRST_LVL_TT_COUN*SECON_LVL_TT_SIZE))

#define SYS_TIMER_CS		0x20003000U
#define SYS_TIMER_CLO		0x20003004U
#define SYS_TIMER_CHI		0x20003008U
#define SYS_TIMER_C0		0x2000300CU
#define SYS_TIMER_C1		0x20003010U
#define SYS_TIMER_C2		0x20003014U
#define SYS_TIMER_C3		0x20003018U


/*
 * Fonctions externes implémentées en assembleurs
 */
/* Permet d'obtenir la base de la pile du supervisor */
extern unsigned int GetSPVStackBase(void);
extern void Set32(unsigned int add, int val);
extern int Get32(unsigned int add);
extern void StartMMU(unsigned int table_base_mmu);


/* Crée la table des pages du noyeau */
unsigned int init_kern_translation_table(void)
{
	/* On place la table des pages juste après la pile du supervieur */
	unsigned int tt_ptr;
	unsigned int i, j;
	const unsigned int base_tt_ptr = GetSPVStackBase();
	const unsigned int device_flags = (1U << 11U) | (1U << 10U) | (1U << 4U)
									| (1U << 2U)  | (1U << 1U) | (1U << 0U);

	const unsigned int normal_flags = (1U << 11U) | (1U << 6U)
									| (1U << 4U)  | (1U << 3U) | (1U << 2U)
									| (1U << 1U);
	
	tt_ptr = base_tt_ptr;

	/* On crée la table de niveau 1 */
	for (i = 0U; i < FIRST_LVL_TT_COUN; i++)
	{
		Set32(tt_ptr, (base_tt_ptr + FIRST_LVL_TT_SIZE + i*SECON_LVL_TT_SIZE) | (1U << 0U));
		tt_ptr += 4U;
	}

	/* tt_ptr = base_tt_ptr + FIRST_LVL_TT_SIZE */

	/* On crée les table de niveau 2 */
	for (i = 0U; i < FIRST_LVL_TT_COUN; i++)
	{
		for (j = 0U; j < SECON_LVL_TT_COUN; j++)
		{
			unsigned int current_frame = i*SECTION_SIZE + j*PAGE_SIZE;
			if ((current_frame >= 0x20000000) && (current_frame < 0x20FFFFFF))
			{ /* Device */
				Set32(tt_ptr, current_frame | device_flags);
			}
			else
			{ /* Normal */
				Set32(tt_ptr, current_frame | normal_flags);
			}
			tt_ptr += 4U;
		}
	}

	return base_tt_ptr;
}


void ActiveWait(unsigned int time_to_wait)
{
	Set32(SYS_TIMER_C0, Get32(SYS_TIMER_CLO) + time_to_wait);

	while ((Get32(SYS_TIMER_CS) & 1U) == 0U)
	{
		// On attend...
	}
}

void blink(void)
{
	const unsigned int time_to_wait = 200000U;

	TurnLedOn();

	ActiveWait(time_to_wait);

	TurnLedOff();

	ActiveWait(time_to_wait);
}


/* Fonction kmain() */
int kmain(void)
{
	StartMMU(init_kern_translation_table());
	
	InitLed();

	TurnLedOn();

	for ( ;; )
	{
		// RAF
	}

	return 0;
}
